from flask import Flask, request, render_template
from time import time
from datetime import datetime
from json import dumps, loads
from os import path
from csv import writer
from lib import cloud_password_verify_api as cpv_api
from pickle import dump, load, UnpicklingError
from lib import file_lock as lock
app = Flask(__name__)

file_dir = path.dirname(__file__)

number = 0
verify = False
operate_list = []

add_l = ['发言', '帮助同学', '作业', '早读', '眼操', '大课间', '桌椅', '午餐', '课堂作业', '运动会']
add_l1 = ['talk', 'helping', 'homework', 'reading', 'eye', 'big_break', 'desk', 'lunch', 'exercise', 'sport']

minus_l = ['作业', '早读', '眼操', '大课间', '桌椅', '午餐', '课堂作业', '纪律']
minus_l1 = ['_homework', '_reading', '_eye', '_sport', '_desk', '_lunch', '_exercise', '_discipline']

'''
l = ['发言', '帮助同学', '作业', '早读', '眼操', '大课间', '桌椅', '午餐', '课堂作业', '运动会', '作业', '早读', '眼操', '大课间', '桌椅', '午餐', '课堂作业', '纪律'] #项目在GUI上的名称
l1 = ['talk', 'helping', 'homework', 'reading', 'eye', 'big_break', 'desk', 'lunch', 'exercise', 'sport', '_homework', '_reading', '_eye', '_sport', '_desk', '_lunch', '_exercise', '_discipline'] #项目在后端form的名称
l2 = ['+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '-', '-', '-', '-', '-', '-', '-', '-', ] #项目的加分/减分（仅本项目适用）
'''

add_lenl1 = len(add_l1)
minus_lenl1 = len(minus_l1)


apikey = ''
admin_apikey = ''
port = 8091

def load_config():
    global apikey
    global port
    global admin_apikey
    with open(f'{file_dir}/databases/config.txt', 'r') as file:
        config = file.readlines()
    apikey = config[0][:-1]
    admin_apikey = config[1][:-1]
    try:
        port = int(config[2])
    except:
        pass
    


def items():
    x = render_template(
    'items.html',
    value = 30,
    add_l = add_l,
    add_l1 = add_l1,
    minus_l = minus_l,
    minus_l1 = minus_l1,
    add_lenl1 = add_lenl1,
    minus_lenl1 = minus_lenl1,
    #l2 = l2
    )
    return x

@app.route("/")
def index():
    ip = request.remote_addr
    with open(f'{file_dir}/databases/ip_log.csv', 'r') as ip_file:
        ip_csv_list = ip_file.readlines()
        ip_csv_list.reverse()
    if ip_csv_list != []:
        for n in ip_csv_list:
            if ip in n:
                x = n.split(',')
                if time() <= float(x[1]):
                    return items()
                else:
                    return render_template('verify.html', action='/cpv_api')
            else:
                return render_template('verify.html', action='/cpv_api')
    else:
        return render_template('verify.html', action='/cpv_api')
    


@app.route("/cpv_api", methods=['GET', 'POST'])
def cpv_api_lib():
    method = request.method
    ip = request.remote_addr
    usrname = request.form.get('usrname', str)
    password = request.form.get('password', str)

    if method == 'GET':
        with open(f'{file_dir}/databases/ip_log.csv', 'r') as ip_file:
            ip_csv_list = ip_file.readlines()
            ip_csv_list.reverse()
        if ip_csv_list != []:
            for n in ip_csv_list:
                if ip in n:
                    x = n.split(',')
                    if time() <= float(x[1]):
                        return items()
                    else:
                        return render_template('verify.html', action='/cpv_api')
                else:
                    return render_template('verify.html', action='/cpv_api')
        else:
            return render_template('verify.html', action='/cpv_api')
    elif method == 'POST':
        api_key = apikey
        password = str((usrname, password))
        if cpv_api.verify(api_key, password) == False:
            #==========
            #lock test
            #==========
            with open(f'{file_dir}/databases/ip_log.csv', 'a') as csv_file:
                if lock.lock_retry(csv_file, timeout=10):
                    csv_file.write(f'\n{ip}, {time() + 43200}')
                else:
                    #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
                    raise lock.FileLockError('lock') #debug用
                if lock.unlock_retry(csv_file, timeout=10):
                    pass #对了（一般）
                    #pass可以直接走了，就return items
                else:
                    #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
                    raise lock.FileLockError('unlock') #debug用
            #==========
            #lock test
            #==========

            return items()
        else:
            return 'the usrname or password is incorrect.'


'''
@app.route("/items")
def items():
    global number
    number = request.args.get("number")
    return items_html
'''

@app.route("/logged",  methods=['GET', 'POST'])
def logged():
    global number
    global operate_list
    operate_list = []
    stu_list = request.form.getlist('number')
    talk_list = request.form.getlist('talk')
    helping_list = request.form.getlist('helping')
    homework_list = request.form.getlist('homework')
    reading_list = request.form.getlist('reading')
    eye_list = request.form.getlist('eye')
    big_break_list = request.form.getlist('big_break')
    desk_list = request.form.getlist('desk')
    lunch_list = request.form.getlist('lunch')
    exercise_list = request.form.getlist('exercise')
    sport_list = request.form.getlist('sport')
    _homework_list = request.form.getlist('_homework')
    _reading_list = request.form.getlist('_reading')
    _eye_list = request.form.getlist('_eye')
    _sport_list = request.form.getlist('_sport')
    _desk_list = request.form.getlist('_desk')
    _lunch_list = request.form.getlist('_lunch')
    _exercise_list = request.form.getlist('_exercise')
    _discipline_list = request.form.getlist('_discipline')
    for stu in stu_list:
        n = 0
        l = [talk_list, helping_list, homework_list, reading_list, 
              eye_list, big_break_list, desk_list, lunch_list, exercise_list, sport_list, 
              _homework_list, _reading_list, _eye_list, _sport_list, _desk_list, 
              _lunch_list, _exercise_list, _discipline_list] #项目在后端form的名称
        for j in l:
            m = sum([int(i) for i in j])
            operate_list.append(m)
            for k in j:
                n += int(k)
        with open(f'/{file_dir}/databases/json_database.json', 'r') as json_file:
            json_db = json_file.read()
        json_db = loads(json_db)
        point = json_db[stu]
        point = int(point)
        point += n
        point = str(point)
        json_db[stu] = point
        #==========
        #lock test
        #==========
        with open(f'{file_dir}/databases/json_database.json', 'w') as json_file:
            if lock.lock_retry(json_file, timeout=10):
                json_file.write(dumps(json_db))
            else:
                #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
                raise lock.FileLockError('lock') #debug用
            if lock.unlock_retry(json_file, timeout=10):
                pass #对了（一般）
                #pass可以直接走了，就return items
            else:
                #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
                raise lock.FileLockError('unlock') #debug用
        #==========
        #lock test
        #==========

    try:
        # 尝试读取现有数据
        with open(f'/{file_dir}/databases/operate_log.pickle', 'rb') as f:
            op_list = load(f)
    except (FileNotFoundError, EOFError, UnpicklingError):
        # 文件不存在或为空时初始化空列表
        op_list = []

    # 追加新数据
    op_list.append([operate_list, stu_list, n])
    #print(op_list, '\n', operate_list)

    # 重新写入数据（确保每次操作后保存）
    #==========
    #lock test
    #==========
    with open(f'/{file_dir}/databases/operate_log.pickle', 'wb') as f:
        if lock.lock_retry(f, timeout=10):
            dump(op_list, f)
        else:
            #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
            raise lock.FileLockError('lock') #debug用
        if lock.unlock_retry(f, timeout=10):
            pass #对了（一般）
            #pass可以直接走了，就return items
        else:
            #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
            raise lock.FileLockError('unlock') #debug用
    #==========
    #lock test
    #==========


    #用number对应form计算分值，整到json上
    
    #记录转好格式整成csv
    return render_template('logged.html', n=str(n))

"""
    with open(f'{file_dir}/databases/operate_log.pickle', 'rb') as f:
        op_list = load(f)
    op_list.append(operate_list)
    with open(f'{file_dir}/databases/operate_log.pickle', 'wb') as f:
        # 使用最高版本可用协议对 'data' 字典进行 pickle。
        dump(op_list, f)
"""

def admin_page():
    with open(f'{file_dir}/databases/json_database.json', 'r') as json_file:
        json_db = json_file.read()
    #json_db和op_csv解码并jinja
    json_db = loads(json_db)
    return render_template('admin.html', json_db=json_db, op_list=operate_log(), len_op=len(operate_log()))

def operate_log():
    log_list = []
    try:
        with open(f'/{file_dir}/databases/operate_log.pickle', 'rb') as f:
            op_list = load(f)
            #print(op_list)#
    except (EOFError, UnpicklingError):
        return []
    for operate_list in op_list:
        l = [
            ('发言+', operate_list[0][0]), 
            (' 帮助同学+', operate_list[0][1]), 
            (' 作业+', operate_list[0][2]), 
            (' 早读+', operate_list[0][3]), 
            (' 眼操+', operate_list[0][4]), 
            (' 大课间+', operate_list[0][5]), 
            (' 桌椅+', operate_list[0][6]), 
            (' 午餐+', operate_list[0][7]), 
            (' 课堂作业+', operate_list[0][8]), 
            (' 运动会+', operate_list[0][9]), 
            (' 作业', operate_list[0][10]), 
            (' 早读', operate_list[0][11]), 
            (' 眼操', operate_list[0][12]), 
            (' 大课间', operate_list[0][13]), 
            (' 桌椅', operate_list[0][14]), 
            (' 午餐', operate_list[0][15]), 
            (' 课堂作业', operate_list[0][16]), 
            (' 纪律', operate_list[0][17])
            ]

        l1 = []
        for m in l:
            if m[1] != 0:
                l1.append(m)
        l = l1

        item = ''
        for i in range(len(l)):
            item += f'{l[i][0]}{l[i][1]} '
        
        stu = ''
        for i in operate_list[1]:
            stu += f'{i}号 '

        item = stu + item

        log_list.append(item)
    return log_list

'''
@app.route("/operate_log") #test
def op_log():
    return operate_log()
'''

@app.route("/admin_verify", methods=['GET', 'POST'])
def admin_verify():
    ip = request.remote_addr
    with open(f'{file_dir}/databases/admin_ip_log.csv', 'r') as admin_ip_file:
        admin_ip_csv_list = admin_ip_file.readlines()
        admin_ip_csv_list.reverse()
    if admin_ip_csv_list != []:
        for n in admin_ip_csv_list:
            if ip in n:
                x = n.split(',')
                if time() <= float(x[1]):
                    return admin_page()
                else:
                    return render_template('verify.html', action='/admin')
            else:
                return render_template('verify.html', action='/admin')
    else:
        return render_template('verify.html', action='/admin')

@app.route("/admin", methods=['GET', 'POST'])
def admin():
    method = request.method
    ip = request.remote_addr
    usrname = request.form.get('usrname', str)
    password = request.form.get('password', str)

    if method == 'GET':
        with open(f'{file_dir}/databases/admin_ip_log.csv', 'r') as admin_ip_file:
            admin_ip_csv_list = admin_ip_file.readlines()
            admin_ip_csv_list.reverse()
        if admin_ip_csv_list != []:
            for n in admin_ip_csv_list:
                if ip in n:
                    x = n.split(',')
                    if time() <= float(x[1]):
                        return admin_page()
                    else:
                        return render_template('verify.html', action='/admin')
                else:
                    return render_template('verify.html', action='/admin')
        else:
            return render_template('verify.html', action='/admin')
    elif method == 'POST':
        api_key = admin_apikey
        password = str((usrname, password))
        if cpv_api.verify(api_key, password) == False:
            #==========
            #lock test
            #==========
            with open(f'{file_dir}/databases/admin_ip_log.csv', 'a') as admin_csv_file:
                if lock.lock_retry(admin_csv_file, timeout=10):
                    admin_csv_file.write(f'\n{ip}, {time() + 43200}')
                else:
                    #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
                    raise lock.FileLockError('lock') #debug用
                if lock.unlock_retry(admin_csv_file, timeout=10):
                    pass #对了（一般）
                    #pass可以直接走了，就return items
                else:
                    #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
                    raise lock.FileLockError('unlock') #debug用
            #==========
            #lock test
            #==========
            return admin_page()
        else:
            return 'the usrname or password is incorrect.'
        
@app.route("/delete", methods=['POST']) #给JS留的API    
def delete():
    button_id = request.get_json('id')
    with open(f'/{file_dir}/databases/operate_log.pickle', 'rb') as f:
        op_list = load(f)
    operate_list = op_list.pop(button_id['id'])
    #==========
    #lock test
    #==========
    with open(f'/{file_dir}/databases/operate_log.pickle', 'wb') as f:
        if lock.lock_retry(f, timeout=10):
            dump(op_list, f)
        else:
            #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
            raise lock.FileLockError('lock') #debug用
        if lock.unlock_retry(f, timeout=10):
            pass #对了（一般）
            #pass可以直接走了，就return items
        else:
            #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
            raise lock.FileLockError('unlock') #debug用
    #==========
    #lock test
    #==========
 
    with open(f'{file_dir}/databases/json_database.json', 'r') as json_file:
        json_db = json_file.read()

    json_db = loads(json_db)
    for stu in operate_list[1]:
        point = json_db[stu]
        point = int(point)
        point -= operate_list[2]
        point = str(point)
        json_db[stu] = point
        #==========
        #lock test
        #==========
        with open(f'/{file_dir}/databases/json_database.json', 'w') as json_file:
            if lock.lock_retry(json_file, timeout=10):
                json_file.write(dumps(json_db))    
            else:
                #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
                raise lock.FileLockError('lock') #debug用
            if lock.unlock_retry(json_file, timeout=10):
                pass #对了（一般）
                #pass可以直接走了，就return items
            else:
                #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
                raise lock.FileLockError('unlock') #debug用
        #==========
        #lock test
        #==========

    return '0'


@app.route("/clear", methods=['POST']) #JS clear json/pickle API
def clear():
    clear_class = request.get_json('class')
    if clear_class['class'] == 'json':
        with open(f'{file_dir}/databases/json_database.json', 'r') as json_file:
            json_db = json_file.read()
        json_db = loads(json_db)
        for item in json_db:
            json_db[item] = '0'

        #==========
        #lock test
        #==========
        with open(f'/{file_dir}/databases/json_database.json', 'w') as json_file:
            if lock.lock_retry(json_file, timeout=10):
                json_file.write(dumps(json_db))  
            else:
                #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
                raise lock.FileLockError('lock') #debug用
            if lock.unlock_retry(json_file, timeout=10):
                pass #对了（一般）
                #pass可以直接走了，就return items
            else:
                #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
                raise lock.FileLockError('unlock') #debug用
        #==========
        #lock test
        #==========  

    elif clear_class['class'] == 'pickle':
        try:
            # 尝试读取现有数据
            with open(f'/{file_dir}/databases/operate_log.pickle', 'rb') as f:
                op_list = load(f)
        except (FileNotFoundError, EOFError, UnpicklingError):
            # 文件不存在或为空时初始化空列表
            op_list = []

        # 追加新数据
        op_list = []
        #print(op_list, '\n', operate_list)

        # 重新写入数据（确保每次操作后保存）
    #==========
    #lock test
    #==========
    with open(f'/{file_dir}/databases/operate_log.pickle', 'wb') as f:
        if lock.lock_retry(f, timeout=10):
            dump(op_list, f)
        else:
            #pass #就是异常了，想个办法（如挂起线程/抛回浏览器/重试（如while到返回值为True就退））
            raise lock.FileLockError('lock') #debug用
        if lock.unlock_retry(f, timeout=10):
            pass #对了（一般）
            #pass可以直接走了，就return items
        else:
            #pass #如果上面对了，这里应该是可以的。不对的话参见上面的方法
            raise lock.FileLockError('unlock') #debug用
    #==========
    #lock test
    #==========

    return '0'


@app.route("/three_confirms")
def three_confirms():
    return render_template('three_confirms.html')


if __name__ == "__main__":
    load_config()
    app.run(host='0.0.0.0', port=port, debug=True)